home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / HPACK78S.ZIP / filesys.c < prev    next >
C/C++ Source or Header  |  1992-12-03  |  40KB  |  1,200 lines

  1. /****************************************************************************
  2. *                                                                            *
  3. *                            HPACK Multi-System Archiver                        *
  4. *                            ===========================                        *
  5. *                                                                            *
  6. *                        Filesystem-Specific Support Routines                *
  7. *                            FILESYS.C  Updated 03/09/92                        *
  8. *                                                                            *
  9. * This program is protected by copyright and as such any use or copying of    *
  10. *  this code for your own purposes directly or indirectly is highly uncool    *
  11. *                      and if you do so there will be....trubble.                *
  12. *                 And remember: We know where your kids go to school.            *
  13. *                                                                            *
  14. *        Copyright 1990 - 1992  Peter C.Gutmann.  All rights reserved        *
  15. *                                                                            *
  16. ****************************************************************************/
  17.  
  18. #include <ctype.h>
  19. #include <string.h>
  20. #ifdef __MAC__
  21.   #include "defs.h"
  22.   #include "arcdir.h"
  23.   #include "error.h"
  24.   #include "flags.h"
  25.   #include "frontend.h"
  26.   #include "hpacklib.h"
  27.   #include "hpaktext.h"
  28.   #include "system.h"
  29.   #include "tags.h"
  30.   #include "fastio.h"
  31.   #include "hpackio.h"
  32. #else
  33.   #include "defs.h"
  34.   #include "arcdir.h"
  35.   #include "error.h"
  36.   #include "flags.h"
  37.   #include "frontend.h"
  38.   #include "hpacklib.h"
  39.   #include "system.h"
  40.   #include "tags.h"
  41.   #include "io/fastio.h"
  42.   #include "io/hpackio.h"
  43.   #include "language/hpaktext.h"
  44. #endif /* __MAC__ */
  45.  
  46. #ifdef __OS2__
  47.  
  48. /* Prototypes for functions in OS2.C */
  49.  
  50. void setAccessTime( const char *filePath, const LONG timeStamp );
  51. void setCreationTime( const char *filePath, const LONG timeStamp );
  52. void addLongName( const char *filePath, const char *longName );
  53. void setEAinfo( const char *filePath, const TAGINFO *tagInfo, const FD srcFD );
  54.  
  55. #endif /* __OS2__ */
  56.  
  57. /* Take a full pathname, convert it to an OS-compatible format, and extract
  58.    out the path component of the name */
  59.  
  60. int extractPath( char *fullPath, char *pathName )
  61.     {
  62. #if defined( __AMIGA__ ) || defined( __ARC__ ) || defined( __ATARI__ ) || \
  63.     defined( __MAC__ ) || defined( __MSDOS__ ) || defined( __OS2__ ) || \
  64.     defined( __VMS__ )
  65.     BOOLEAN hasDeviceSpec = FALSE;
  66. #endif /* __AMIGA__ || __ARC__ || __ATARI__ || __MAC__ || __MSDOS__ || __OS2__ || __VMS__ */
  67.     int count, lastSlash = ERROR;
  68.  
  69. #if defined( __ATARI__ ) || defined( __MSDOS__ ) || defined( __OS2__ )
  70.     /* Check whether there is a drive specifier prepended to the path */
  71.     if( fullPath[ 1 ] == ':' )
  72.         {
  73.         lastSlash = 1;
  74.         hasDeviceSpec = TRUE;
  75.         }
  76. #endif /* __MSDOS__ || __OS2__ */
  77. #pragma run "/usr/games/moria"
  78.  
  79.     /* Separate the path and filename components, converting directory
  80.        seperators into an OS-compatible format at the same time */
  81.     for( count = 0; fullPath[ count ]; count++ )
  82.         {
  83.         fullPath[ count ] = caseConvert( fullPath[ count ] );
  84. #if defined( __MSDOS__ )
  85.         /* Fix compiler bug */
  86. #elif defined( __ATARI__ ) || defined( __OS2__ )
  87.         if( fullPath[ count ] == '\\' )
  88.             fullPath[ count ] = SLASH;
  89. #endif /* __ATARI__ || __OS2__ */
  90. #if defined( __AMIGA__ ) || defined( __ARC__ ) || defined( __MAC__ ) || defined( __VMS__ )
  91.         if( fullPath[ count ] == ':' )
  92.             {
  93.             hasDeviceSpec = TRUE;
  94.             lastSlash = count;
  95.             }
  96. #endif /* __AMIGA__ || __ARC__ || __MAC__ || __VMS__ */
  97.         if( fullPath[ count ] == SLASH )
  98.             lastSlash = count;
  99.         }
  100.  
  101.     /* If there is only a device/node spec or only a device/node spec and a
  102.        slash we must handle it differently than a normal path:  We can't
  103.        stomp the char at lastSlash as we would for a normal path */
  104. #if defined( __ATARI__ ) || defined( __MSDOS__ ) || defined( __OS2__ )
  105.     if( hasDeviceSpec && ( lastSlash == 1 || lastSlash == 2 ) )
  106.         {
  107.         strncpy( pathName, fullPath, ++lastSlash );
  108.         pathName[ lastSlash ] = '\0';
  109.         }
  110.     else
  111. #elif defined( __AMIGA__ ) || defined( __ARC__ ) || defined( __MAC__ ) || \
  112.       defined( __VMS__ )
  113.     if( hasDeviceSpec && lastSlash != ERROR && \
  114.       ( fullPath[ lastSlash ] == ':' || ( lastSlash && fullPath[ lastSlash - 1 ] == ':' ) ) )
  115.         {
  116.         strncpy( pathName, fullPath, ++lastSlash );
  117.         pathName[ lastSlash ] = '\0';
  118.         }
  119.     else
  120. #endif /* Various OS-specific drive handling */
  121.         if( lastSlash > 0 )
  122.             {
  123.             /* Create the pathname, stomping the last SLASH for the
  124.                directory-handling functions */
  125.             strncpy( pathName, fullPath, lastSlash );
  126.             pathName[ lastSlash++ ] = '\0';
  127.             }
  128.         else
  129.             {
  130.             if( !lastSlash )
  131.                 /* Special case:  File is off the root dir */
  132.                 *pathName = SLASH;
  133.             pathName[ ++lastSlash ] = '\0';
  134.             }
  135.  
  136.     return( lastSlash );
  137.     }
  138.  
  139. /* Find the filename component of a given filePath */
  140.  
  141. char *findFilenameStart( char *filePath )
  142.     {
  143.     char *filePtr = filePath;
  144.     int i;
  145.  
  146.     /* Skip prepended device/node specifier if necessary */
  147. #if defined( __ATARI__ ) || defined( __MSDOS__ ) || defined( __OS2__ )
  148.     if( filePtr[ 1 ] == ':' )
  149.         filePtr += 2;
  150. #elif defined( __AMIGA__ ) || defined( __MAC__ ) || defined( __VMS__ )
  151.     for( i = 0; filePath[ i ]; i++ )
  152.         if( filePath[ i ] == ':' )
  153.             {
  154.             filePtr = filePath + i + 1;    /* +1 to skip ':' */
  155.             break;    /* Exit after first colon */
  156.             }
  157. #endif /* Various OS-specific device/node specifier skips */
  158.  
  159.     /* Find the first char after the last SLASH */
  160.     for( i = 0; filePtr[ i ]; i++ )
  161.         if( filePtr[ i ] == SLASH )
  162.             filePtr += i + 1;    /* +1 to skip SLASH */
  163.  
  164.     return( filePtr );
  165.     }
  166.  
  167. #if 0
  168. /* Using a given filename, build a path to the directory for temporary files */
  169.  
  170. BOOLEAN makeTempPath( const char *fileName, char *tempPath )
  171.     {
  172.     char *tmpPath;
  173.  
  174.     /* Build path to keyring file */
  175.     *tempPath = '\0';
  176.     if( ( tmpPath = getenv( TMPPATH ) ) != NULL )
  177.         {
  178.         if( strlen( tmpPath ) + fileName > MAX_PATH )
  179.             error( PATH_ss_TOO_LONG, tmpPath, fileName );
  180.         strcpy( tempPath, tmpPath );
  181.         if( ( i = tempPath[ strlen( tempPath ) - 1 ] ) != SLASH && i != '\\' )
  182.             /* Add trailing slash if necessary */
  183.             strcat( tempPath, SLASH_STR );
  184.         strcat( tempPath, fileName );
  185.         return( TRUE );
  186.         }
  187.  
  188.     /* Couldn't create temp path */
  189.     return( FALSE );
  190.     }
  191. #endif /* 0 */
  192.  
  193. /* Check whether a given directory exists */
  194.  
  195. static BOOLEAN dirnameConflict;        /* Whether there is a file/directory
  196.                                        name conflict */
  197. BOOLEAN dirExists( char *pathName )
  198.     {
  199.     FILEINFO pathInfo;
  200.     BOOLEAN retVal = FALSE;
  201.     char dirPath[ MAX_PATH ];
  202. #if defined( __AMIGA__ ) || defined( __ARC__ ) || defined( __ATARI__ ) || \
  203.     defined( __MAC__ ) || defined( __MSDOS__ ) || defined( __OS2__ )
  204.     int strEnd;
  205. #endif /* __AMIGA__ || __ARC__ || __ATARI__ || __MAC__ || __MSDOS__ || __OS2__ */
  206.  
  207.     dirnameConflict = FALSE;
  208.  
  209.     /* Convert the path into an OS-compatible format */
  210.     extractPath( pathName, dirPath );
  211.  
  212. #if defined( __AMIGA__ ) || defined( __ARC__ ) || defined( __ATARI__ ) || \
  213.     defined( __MAC__ ) || defined( __MSDOS__ ) || defined( __OS2__ )
  214.     /* If the pathName is a drive spec only, we need to add a dummy arg.
  215.        The test for whether we need to add the MATCH_ALL is safe since
  216.        extractPath will only leave a trailing slash if the path component
  217.        is a pure drive spec.  When we do the findFirst() we will always
  218.        match ".", so there is no need for special-case processing for this */
  219.     strEnd = strlen( dirPath ) - 1;
  220.     if( dirPath[ strEnd ] == ':' || dirPath[ strEnd ] == SLASH )
  221.         strcat( dirPath, MATCH_ALL );
  222. #endif /* __AMIGA__ || __ARC__ || __ATARI__ || __MAC__ || __MSDOS__ || __OS2__ */
  223.  
  224.     /* See if we can find the given directory */
  225.     if( findFirst( dirPath, ALLFILES_DIRS, &pathInfo ) )
  226.         retVal = TRUE;
  227.     findEnd( &pathInfo );
  228.  
  229.     /* Make sure what we've found is a directory */
  230.     if( retVal && !isDirectory( pathInfo ) )
  231.         /* A filesystem entity of this name already exists */
  232.         dirnameConflict = retVal = TRUE;
  233.  
  234.     return( retVal );
  235.     }
  236.  
  237. /* Filter out illegal chars.  This is virtually identical for the Amiga, MSDOS,
  238.    OS/2, and Unix, the only differences being '#' for the Amiga, ':', ';',
  239.    '+', ',', '=' for the Atari ST, DOS and OS/2, and '&', '!' for Unix.  The
  240.    Mac only has ':' as an illegal char, the Arc has its own wierd selection,
  241.    and VMS has everything except '_' illegal.  Note that the space char must
  242.    be the first one in the exclusion list if it's used */
  243.  
  244. #if defined( __MSDOS__ )
  245.   #define BAD_CHARS            " \"*+,/:;<=>?[\\]|"
  246. #elif defined( __AMIGA__ )
  247.   #define BAD_CHARS            "\"#$/:<>?|"
  248. #elif defined( __ARC__ )
  249.   #define BAD_CHARS            " \"#$%&*.:^"
  250. #elif defined( __ATARI__ )
  251.   #define BAD_CHARS            " \"*+,/:;<=>?[\\]|"
  252. #elif defined( __MAC__ )
  253.   #define BAD_CHARS            ":"
  254. #elif defined( __OS2__)
  255.   #define BAD_CHARS            " \"*+,/:;<=>?[\\]|"
  256. #elif defined( __UNIX__ )
  257.   #define BAD_CHARS            " !\"$&'*/;<>?[\\]`{|}"
  258. #elif defined( __VMS__ )
  259.   #define BAD_CHARS            " !\"#$%&'()*+,-/:;<=>?@[\\]^`{|}~"
  260. #else
  261.   #error "Need to define BAD_CHARS in FILESYS.C"
  262. #endif /* Various OS-specific illegal filename chars */
  263.  
  264. static inline char xlateChar( const char ch )
  265.     {
  266.     int i;
  267.  
  268.     /* Simple rejection check for non basic ASCII characters */
  269.     if( ch < ' ' || ch > '~' )
  270.         return( '_' );
  271.  
  272.     /* Check for character in exclusion list.  OS/2 sets SPACE_OK
  273.        dynamically depending on the filesystem in use */
  274.     for( i = sizeof( BAD_CHARS ) - 2; i >= SPACE_OK; i-- )
  275.         if( ch == BAD_CHARS[ i ] )
  276.             return( '_' );
  277.  
  278.     return( ch );
  279.     }
  280.  
  281. /* Intelligently truncate a filename to eight-dot-three format for MSDOS,
  282.    MAX_FILENAME for the Amiga, the Mac, and Unix, or eight-dot-three/
  283.    MAX_FILENAME for OS/2 FAT/HPFS.  The MSDOS version goes to great lengths
  284.    to produce an optimal translation (ie the best a human could do); the
  285.    Amiga/Mac/Unix versions don't do so well due to the more free-form nature
  286.    of Amiga/Mac/Unix filenames.  In any case suffixes of up to three
  287.    characters are preserved (.c, .h, .cpp), otherwise the name is truncated
  288.    at MAX_FILENAME.  The VMS one is somewhat complicated since we need to
  289.    truncate either to a rather limited 39.39 format or the canonical RSX-11
  290.    format, with additional complications if the pathname component is a
  291.    directory.  The Arc one just truncates down to 10 chars with no extensions,
  292.    which is the ADFS limit and not necessarily imposed by an IFS.  However
  293.    under Risc-OS 2 there is no way to tell exactly what to truncate to so we
  294.    use the worst-case length */
  295.  
  296. #ifdef __VMS__
  297.   static BOOLEAN isDirComponent;    /* dir.components get handled specially */
  298. #endif /* __VMS__ */
  299.  
  300. static BOOLEAN fileNameTruncated;    /* Used by isTruncated() */
  301.  
  302. static char *truncFileName( char *fileName )
  303.     {
  304.     int i, lastDit, fileNameLength = strlen( fileName );
  305.     static char truncName[ MAX_FILENAME ];
  306. #if defined( __AMIGA__ ) || defined( __IIGS__ ) || defined( __ATARI__ ) || \
  307.     defined( __MAC__ ) || defined( __MSDOS__ ) || defined( __OS2__ ) || \
  308.     defined( __UNIX__ ) || defined( __VMS__ )
  309.     int j;
  310. #endif /* __AMIGA__ || __ATARI__ || __IIGS__ || __MAC__ || __MSDOS__ || __OS2__ || __UNIX__ || __VMS__ */
  311. #if defined( __ATARI__ ) || defined( __MSDOS__ ) || defined( __OS2__ ) || \
  312.     defined( __VMS__ )
  313.     char ch;
  314. #endif /* __ATARI__ || __MSDOS__ || __OS2__ || __VMS__ */
  315. #ifdef __VMS__
  316.     int versionNoStart = 0;
  317. #endif /* __VMS__ */
  318.  
  319.     /* Sanity check in case we're called with a dummy filename */
  320.     if( !fileNameLength )
  321.         return( "" );
  322.  
  323. #ifdef __VMS__
  324.     if( !isDirComponent )
  325.         /* Prescan the filename for a version number suffix */
  326.         for( i = fileNameLength - 1; i; i-- )
  327.             if( fileName[ i ] == ';' )
  328.                 {
  329.                 /* Found a possible suffix, check it's valid */
  330.                 for( j = i + 1; j < fileNameLength; j++ )
  331.                     if( fileName[ j ] < '0' || fileName[ j ] > '9' )
  332.                         break;
  333.  
  334.                 /* If it's a valid suffix, don't include it in
  335.                    further calculations */
  336.                 if( j == fileNameLength )
  337.                     {
  338.                     versionNoStart = fileNameLength = i;
  339.                     break;
  340.                     }
  341.                 }
  342. #endif /* __VMS__ */
  343.  
  344.     /* Find last dit */
  345.     for( lastDit = fileNameLength - 1; lastDit && fileName[ lastDit ] != '.'; \
  346.          lastDit-- );
  347. #if defined( __ATARI__ ) || defined( __MSDOS__ )
  348.     if( !lastDit || fileName[ fileNameLength - 1 ] == '.' )
  349.         lastDit = fileNameLength;
  350.  
  351.     /* Copy across first 8 chars or up to lastDit */
  352.     for( i = 0; i < 8 && i < lastDit && i < fileNameLength; i++ )
  353.         {
  354.         ch = truncName[ i ] = xlateChar( fileName[ i ] );
  355.         if( ch == '.' )
  356.             truncName[ i ] = '_';
  357.         }
  358.  
  359.     /* Add dit and continue if necessary */
  360.     if( i < fileNameLength && fileName[ fileNameLength - 1 ] != '.' )
  361.         {
  362.         j = i;
  363.         truncName[ i++ ] = '.';
  364.  
  365.         /* Check for no dit present.  Note that we also check for the
  366.            special case of the dit being at MAX_FILENAME since the
  367.            lastDit loop can stop either at fileNameLength or on a dit */
  368.         if( lastDit == MAX_FILENAME && fileName[ MAX_FILENAME ] != '.' )
  369.             {
  370.             /* No dit, copy across next 3 chars */
  371.             while( i < MAX_FILENAME - 1 && i < fileNameLength )
  372.                 truncName[ i++ ] = xlateChar( fileName[ j++ ] );
  373.             }
  374.         else
  375.             {
  376.             /* Copy across last 1-3 chars after dit */
  377.             for( j = 0; fileName[ fileNameLength - 1 ] != '.' && j < 3 && \
  378.                  i <= fileNameLength; fileNameLength--, j++ );
  379.             while( j-- )
  380.                 truncName[ i++ ] = xlateChar( fileName[ fileNameLength++ ] );
  381.             }
  382.         }
  383. #elif defined( __AMIGA__ ) || defined( __UNIX__ )
  384.     if( !lastDit || fileName[ fileNameLength - 1 ] == '.' )
  385.         {
  386.         lastDit = fileNameLength;
  387.         j = MAX_FILENAME - 1;
  388.         }
  389.     else
  390.         /* Make sure we preserve at least 3 characters of suffix */
  391.         j = ( MAX_FILENAME - 1 ) - \
  392.             ( ( fileNameLength - lastDit > 3 ) ? 4 : fileNameLength - lastDit );
  393.  
  394.     /* Copy across appropriate no.of chars or up to lastDit */
  395.     for( i = 0; i < j && i < lastDit && i < fileNameLength; i++ )
  396.         truncName[ i ] = xlateChar( fileName[ i ] );
  397.  
  398.     /* Add dit and continue if necessary */
  399.     if( i < fileNameLength && fileName[ fileNameLength - 1 ] != '.' && \
  400.         i < MAX_FILENAME && fileNameLength != lastDit )
  401.         {
  402.         truncName[ i++ ] = '.';
  403.  
  404.         /* Copy across last 1-3 chars after dit */
  405.         for( j = 0; fileName[ fileNameLength - 1 ] != '.' && \
  406.              i <= fileNameLength; fileNameLength--, j++ );
  407.         if( j > 3 && fileNameLength == MAX_FILENAME )
  408.             j = 3;        /* Handle overshoot */
  409.         while( j-- )
  410.             truncName[ i++ ] = xlateChar( fileName[ fileNameLength++ ] );
  411.         }
  412. #elif defined( __ARC__ )
  413.     /* Just copy across the first 10 chars (the ADFS limit) */
  414.     for( i = 0; i < 10 && i < fileNameLength; i++ )
  415.         truncName[ i ] = xlateChar( fileName[ i ] );
  416. #elif defined( __MAC__ )
  417.     if( !lastDit || fileName[ fileNameLength - 1 ] == '.' )
  418.         {
  419.         lastDit = fileNameLength;
  420.         j = MAX_FILENAME - 1;
  421.         }
  422.     else
  423.         /* Make sure we preserve at least 3 characters of suffix */
  424.         j = ( MAX_FILENAME - 1 ) - \
  425.             ( ( fileNameLength - lastDit > 3 ) ? 4 : fileNameLength - lastDit );
  426.  
  427.     /* Copy across appropriate no.of chars or up to lastDit */
  428.     for( i = 0; i < j && i < lastDit && i < fileNameLength; i++ )
  429.         truncName[ i ] = xlateChar( fileName[ i ] );
  430.  
  431.     /* If the name starts with a dit, zap it (device names start with dits) */
  432.     if( truncName[ 0 ] == '.' )
  433.         truncName[ 0 ] = '_';
  434.  
  435.     /* Add dit and continue if necessary */
  436.     if( i < fileNameLength && fileName[ fileNameLength - 1 ] != '.' && \
  437.         i < MAX_FILENAME && fileNameLength != lastDit )
  438.         {
  439.         j = i;
  440.         truncName[ i++ ] = '.';
  441.  
  442.         /* Copy across last 1-3 chars after dit */
  443.         for( j = 0; fileName[ fileNameLength - 1 ] != '.' && \
  444.              i <= fileNameLength; fileNameLength--, j++ );
  445.         if( j > 3 && fileNameLength == MAX_FILENAME )
  446.             j = 3;        /* Handle overshoot */
  447.         while( j-- )
  448.             truncName[ i++ ] = xlateChar( fileName[ fileNameLength++ ] );
  449.         }
  450. #elif defined( __OS2__ )
  451.     if( destIsHPFS )
  452.         {
  453.         /* Truncate to HPFS specs */
  454.         if( !lastDit || fileName[ fileNameLength - 1 ] == '.' )
  455.             {
  456.             lastDit = fileNameLength;
  457.             j = MAX_FILENAME - 1;
  458.             }
  459.         else
  460.             /* Make sure we preserve at least 3 characters of suffix */
  461.             j = ( MAX_FILENAME - 1 ) - \
  462.                 ( ( fileNameLength - lastDit > 3 ) ? 4 : fileNameLength - lastDit );
  463.  
  464.         /* Copy across appropriate no.of chars or up to lastDit */
  465.         for( i = 0; i < j && i < lastDit && i < fileNameLength; i++ )
  466.             truncName[ i ] = xlateChar( fileName[ i ] );
  467.  
  468.         /* Add dit and continue if necessary */
  469.         if( i < fileNameLength && fileName[ fileNameLength - 1 ] != '.' && \
  470.             i < MAX_FILENAME && fileNameLength != lastDit )
  471.             {
  472.             j = i;
  473.             truncName[ i++ ] = '.';
  474.  
  475.             /* Copy across last 1-3 chars after dit */
  476.             for( j = 0; fileName[ fileNameLength - 1 ] != '.' && \
  477.                  i <= fileNameLength; fileNameLength--, j++ );
  478.             if( j > 3 && fileNameLength == MAX_FILENAME )
  479.                 j = 3;        /* Handle overshoot */
  480.             while( j-- )
  481.                 truncName[ i++ ] = xlateChar( fileName[ fileNameLength++ ] );
  482.             }
  483.         }
  484.     else
  485.         {
  486.         /* Truncate to 8.3 */
  487.         if( !lastDit || fileName[ fileNameLength - 1 ] == '.' )
  488.             lastDit = fileNameLength;
  489.  
  490.         /* Copy across first 8 chars or up to lastDit */
  491.         for( i = 0; i < 8 && i < lastDit && i < fileNameLength; i++ )
  492.             {
  493.             ch = truncName[ i ] = xlateChar( fileName[ i ] );
  494.             if( ch == '.' )
  495.                 truncName[ i ] = '_';
  496.             }
  497.  
  498.         /* Add dit and continue if necessary */
  499.         if( i < fileNameLength && fileName[ fileNameLength - 1 ] != '.' )
  500.             {
  501.             j = i;
  502.             truncName[ i++ ] = '.';
  503.  
  504.             /* Check for no dit present.  Note that we also check for the
  505.                special case of the dit being at MAX_FILENAME since the
  506.                lastDit loop can stop either at fileNameLength or on a dit */
  507.             if( lastDit == MAX_FILENAME && fileName[ MAX_FILENAME ] != '.' )
  508.                 {
  509.                 /* No dit, copy across next 3 chars */
  510.                 while( i < MAX_FILENAME - 1 && i < fileNameLength )
  511.                     truncName[ i++ ] = xlateChar( fileName[ j++ ] );
  512.                 }
  513.             else
  514.                 {
  515.                 /* Copy across last 1-3 chars after dit */
  516.                 for( j = 0; fileName[ fileNameLength - 1 ] != '.' && j < 3 && \
  517.                      i <= fileNameLength; fileNameLength--, j++ );
  518.                 while( j-- )
  519.                     truncName[ i++ ] = xlateChar( fileName[ fileNameLength++ ] );
  520.                 }
  521.             }
  522.         }
  523. #elif defined( __VMS__ )
  524.     /* Check whether we need to truncate to RSX-11 format */
  525.     if( sysSpecFlags & SYSPEC_RSX11 )
  526.         {
  527.         /* If it's a directory, just copy across up to the first 9 chars */
  528.         if( isDirComponent )
  529.             for( i = 0; i < 9 && i < fileNameLength; i++ )
  530.                 {
  531.                 ch = truncName[ i ] = xlateChar( fileName[ i ] );
  532.                 if( ch == '.' )
  533.                     truncName[ i ] = '_';
  534.                 }
  535.         else
  536.             {
  537.             /* Truncate to 9.3 */
  538.             if( !lastDit || fileName[ fileNameLength - 1 ] == '.' )
  539.                 lastDit = fileNameLength;
  540.  
  541.             /* Copy across first 9 chars or up to lastDit */
  542.             for( i = 0; i < 9 && i < lastDit && i < fileNameLength; i++ )
  543.                 {
  544.                 ch = truncName[ i ] = xlateChar( fileName[ i ] );
  545.                 if( ch == '.' )
  546.                     truncName[ i ] = '_';
  547.                 }
  548.  
  549.             /* Add dit and continue if necessary */
  550.             if( i < fileNameLength && fileName[ fileNameLength - 1 ] != '.' )
  551.                 {
  552.                 j = i;
  553.                 truncName[ i++ ] = '.';
  554.  
  555.                 /* Check for no dit present.  Note that we also check for the
  556.                    special case of the dit being at MAX_FILENAME since the
  557.                    lastDit loop can stop either at fileNameLength or on a dit */
  558.                 if( lastDit == MAX_FILENAME && fileName[ MAX_FILENAME ] != '.' )
  559.                     {
  560.                     /* No dit, copy across next 3 chars */
  561.                     while( i < MAX_FILENAME - 1 && i < fileNameLength )
  562.                         truncName[ i++ ] = xlateChar( fileName[ j++ ] );
  563.                     }
  564.                 else
  565.                     {
  566.                     /* Copy across last 1-3 chars after dit */
  567.                     for( j = 0; fileName[ fileNameLength - 1 ] != '.' && j < 3 && \
  568.                          i <= fileNameLength; fileNameLength--, j++ );
  569.                     while( j-- )
  570.                         truncName[ i++ ] = xlateChar( fileName[ fileNameLength++ ] );
  571.                     }
  572.                 }
  573.             }
  574.         }
  575.     else
  576.         {
  577.         /* If it's a directory, just copy across up to the first 39 chars */
  578.         if( isDirComponent )
  579.             for( i = 0; i < 39 && i < fileNameLength; i++ )
  580.                 {
  581.                 ch = truncName[ i ] = xlateChar( fileName[ i ] );
  582.                 if( ch == '.' )
  583.                     truncName[ i ] = '_';
  584.                 }
  585.         else
  586.             {
  587.             /* Truncate to 39.39 */
  588.             if( !lastDit || fileName[ fileNameLength - 1 ] == '.' )
  589.                 lastDit = fileNameLength;
  590.  
  591.             /* Copy across first 39 chars or up to lastDit */
  592.             for( i = 0; i < 39 && i < lastDit && i < fileNameLength; i++ )
  593.                 {
  594.                 ch = truncName[ i ] = xlateChar( fileName[ i ] );
  595.                 if( ch == '.' )
  596.                     truncName[ i ] = '_';
  597.                 }
  598.  
  599.             /* Add dit and continue if necessary */
  600.             if( i < fileNameLength && fileName[ fileNameLength - 1 ] != '.' )
  601.                 {
  602.                 j = i;
  603.                 truncName[ i++ ] = '.';
  604.  
  605.                 /* Check for no dit present.  Note that we also check for the
  606.                    special case of the dit being at MAX_FILENAME since the
  607.                    lastDit loop can stop either at fileNameLength or on a dit */
  608.                 if( lastDit == MAX_FILENAME && fileName[ MAX_FILENAME ] != '.' )
  609.                     {
  610.                     /* No dit, copy across next 39 chars */
  611.                     while( i < MAX_FILENAME - 1 && i < fileNameLength )
  612.                         truncName[ i++ ] = xlateChar( fileName[ j++ ] );
  613.                     }
  614.                 else
  615.                     {
  616.                     /* Copy across last 1-39 chars after dit */
  617.                     for( j = 0; fileName[ fileNameLength - 1 ] != '.' && j < 39 && \
  618.                          i <= fileNameLength; fileNameLength--, j++ );
  619.                     while( j-- )
  620.                         truncName[ i++ ] = xlateChar( fileName[ fileNameLength++ ] );
  621.                     }
  622.                 }
  623.             }
  624.         }
  625.  
  626.     /* Finally, append the version number if there is one */
  627.     if( versionNoStart )
  628.         while( fileName[ fileNameLength ] )
  629.             truncName[ i++ ] = fileName[ fileNameLength++ ];
  630. #else
  631.     #error "Need to implement truncFileName() in FILESYS.C"
  632. #endif /* Various OS-specific defines */
  633.     truncName[ i ] = '\0';
  634.  
  635.     fileNameTruncated = strcmp( fileName, truncName );
  636.     return( truncName );
  637.     }
  638.  
  639. /****************************************************************************
  640. *                                                                            *
  641. *                            File Type Handling                                *
  642. *                                                                            *
  643. ****************************************************************************/
  644.  
  645. #if defined( __ARC__ ) || defined( __IIGS__ ) || defined( __MAC__ )
  646.  
  647. /* Routines to handle user-defined type settings.  These are extension-type
  648.    associations of the form <file extension> = <type information>, and can
  649.    be used to add new types and override built-in ones */
  650.  
  651. #define EXT_LENGTH            8                /* 7 chars + '\0' */
  652.  
  653. #if defined( __ARC__ )
  654.   #define TYPE_LENGTH        sizeof( WORD )    /* Length of file type field */
  655.   #define CREATOR_LENGTH    0                /* No secondary type */
  656. #elif defined( __IIGS__ )
  657.   #define TYPE_LENGTH        sizeof( WORD )    /* Length of file type field */
  658.   #define CREATOR_LENGTH    sizeof( LONG )    /* Length of auxType field */
  659. #elif defined( __MAC__ )
  660.   #define TYPE_LENGTH        sizeof( LONG )    /* Length of file type field */
  661.   #define CREATOR_LENGTH    sizeof( LONG )    /* Length of file creator field */
  662. #endif /* Various system-dependant file type sizes */
  663.  
  664. typedef struct TA {
  665.                   struct TA *next;                /* The next header in the list */
  666.                   char extension[ EXT_LENGTH ];    /* The file extension */
  667.                   LONG type;                    /* The file type */
  668. #if defined( __IIGS__ ) || defined( __MAC__ )
  669.                   LONG creator;                    /* The file creator */
  670. #endif /* __IIGS__ || __MAC__ */
  671.                   } TYPE_ASSOC;
  672.  
  673. static TYPE_ASSOC *typeAssocListHead = NULL;
  674.  
  675. void addTypeAssociation( char *assocData )
  676.     {
  677.     TYPE_ASSOC *namePtr, *prevPtr, *newRecord;
  678.     char *typePtr, *creatorPtr;
  679.     char type[ TYPE_LENGTH ];
  680. #if defined( __IIGS__ ) || defined( __MAC__ )
  681.     char creator[ CREATOR_LENGTH ];
  682. #endif /* __IIGS__ || __MAC__ */
  683.  
  684.     /* Break up the assocData string into its component parts */
  685.     for( typePtr = assocData; *typePtr && *typePtr != '='; typePtr++ );
  686.     if( !*typePtr )        /* Check for end of string reached */
  687.         return;
  688.     for( creatorPtr = typePtr; *creatorPtr && *creatorPtr != ','; creatorPtr++ );
  689. #if defined( __IIGS__ ) || defined( __MAC__ )
  690.     if( !*creatorPtr )    /* Check for end of string reached */
  691.         return;
  692. #endif /* __IIGS__ || __MAC__ */
  693.     *typePtr++ = *creatorPtr++ = '\0';    /* Break up string */
  694.  
  695.     /* Perform some simple range checks.  Note that the creatorPtr check
  696.        works even when no creator is present since it checks for spurious
  697.        noise at the end of the string */
  698.     if( strlen( assocData ) > EXT_LENGTH || \
  699.         strlen( creatorPtr ) > CREATOR_LENGTH || \
  700.         strlen( typePtr ) > TYPE_LENGTH )
  701.         return;
  702.  
  703.     /* Pad type and creator with zeroes if necessary */
  704.     memset( type, 0, TYPE_LENGTH );
  705.     memcpy( type, typePtr, TYPE_LENGTH );
  706. #if defined( __IIGS__ ) || defined( __MAC__ )
  707.     memset( creator, 0, CREATOR_LENGTH );
  708.     memcpy( creator, creatorPtr, CREATOR_LENGTH );
  709. #endif /* __IIGS__ || __MAC__ */
  710.  
  711.     for( namePtr = typeAssocListHead; namePtr != NULL; \
  712.          prevPtr = namePtr, namePtr = namePtr->next )
  713.         /* Check whether we've already processed an association of this name */
  714.         if( !strcmp( assocData, namePtr->extension ) )
  715.             return;
  716.  
  717.     /* We have reached the end of the list without a match, so we add this
  718.        archive name to the list */
  719.     if( ( newRecord = ( TYPE_ASSOC * ) hmalloc( sizeof( TYPE_ASSOC ) ) ) == NULL )
  720.         error( OUT_OF_MEMORY );
  721.     strcpy( newRecord->extension, assocData );
  722.     memcpy( &newRecord->type, type, TYPE_LENGTH );
  723. #if defined( __IIGS__ ) || defined( __MAC__ )
  724.     memcpy( &newRecord->creator, creator, CREATOR_LENGTH );
  725. #endif /* __IIGS__ || __MAC__ */
  726.     newRecord->next = NULL;
  727.     if( typeAssocListHead == NULL )
  728.         typeAssocListHead = newRecord;
  729.     else
  730.         prevPtr->next = newRecord;
  731.     return;
  732.     }
  733.  
  734. /* Free up the memory used by the list of archive names */
  735.  
  736. void freeTypeAssociations( void )
  737.     {
  738.     TYPE_ASSOC *namePtr = typeAssocListHead, *headerPtr;
  739.  
  740.     /* A simpler implementation of the following would be:
  741.        for( namePtr = typeAssocListHead; namePtr != NULL; namePtr = namePtr->next )
  742.            hfree( namePtr );
  743.        However this will fail on some systems since we will be trying to
  744.        read namePtr->next out of a record we have just free()'d */
  745.     while( namePtr != NULL )
  746.         {
  747.         headerPtr = namePtr;
  748.         namePtr = namePtr->next;
  749.         hfree( headerPtr );
  750.         }
  751.     }
  752.  
  753. /* Try and determine the type of a file based on the filename suffix.  Note
  754.    that sometimes we can't tell the exact type from the suffix (.hpk, Unix
  755.    executables), and sometimes the formats are incompatible, undocumented,
  756.    and constantly changing (MS Word) so we don't bother */
  757.  
  758. #if defined( __ARC__ )
  759.  
  760. typedef struct {
  761.                char *suffix;            /* The suffix for the file */
  762.                WORD type;                /* The file type */
  763.                } FILE_TYPEINFO;
  764.  
  765. static FILE_TYPEINFO typeInfo[] =
  766.             {
  767.             { "arc", 0x0DDC },            /* ARC archive (Sparc archiver) */
  768.             { "arj", 0x0DDC },            /* ARJ archive (Sparc archiver) */
  769.             { "bat", 0x0FDA },            /* MSDOS batch file */
  770.             { "bmp", 0x069C },            /* Windoze BMP graphics file */
  771.             { "c", 0x0FFF },            /* C code file */
  772.             { "cgm", 0x0405 },            /* Computer Graphics Metafile */
  773.             { "com", 0x0FD8 },            /* MSDOS executable */
  774.             { "csv", 0x0DFE },            /* Comma Seperated Variable file */
  775.             { "dif", 0x0DFD },            /* DIF spreadsheet data */
  776.             { "dbf", 0x0DB3 },            /* dBase III file */
  777.             { "dvi", 0x0CE5 },            /* DVI (actually TeX) file */
  778.             { "dxf", 0x0DEA },            /* DXF portalbe CAD file format */
  779.             { "h", 0x0FFF },            /* C header files */
  780.             { "gif", 0x0695 },            /* GIF graphics file */
  781.             { "exe", 0x0FD9 },            /* MSDOS executable */
  782.             { "iff", 0x0693 },            /* Amiga IFF graphics file */
  783.             { "img", 0x0692 },            /* Atari IMG graphics file */
  784.             { "fits", 0x06A3 },            /* Flexible Image Transport Sys. file */
  785.             { "jpg", 0x0C85 },            /* JPEG graphics file */
  786.             { "lzh", 0x0DDC },            /* Lharc archive (Sparc archiver) */
  787.             { "mac", 0x0694 },            /* MacPaint graphics file */
  788.             { "mod", 0x0CB6 },            /* Amiga .MOD sound file */
  789.             { "ndx", 0x0DB1 },            /* dBase III index file */
  790.             { "pbm", 0x069E },            /* PBM graphics file */
  791.             { "pcx", 0x0697 },            /* PCX graphics file */
  792.             { "pi1", 0x0691 },            /* Atari Degas graphics file */
  793.             { "pi2", 0x0691 },            /* Atari Degas graphics file */
  794.             { "pi3", 0x0691 },            /* Atari Degas graphics file */
  795.             { "pi4", 0x0691 },            /* Atari Degas graphics file */
  796.             { "ps", 0x0FF5 },            /* PostScript file */
  797.             { "qrt", 0x0698 },            /* QRT graphic file. The 'Q' is for 'slow' */
  798.             { "rf", 0x0FC9 },            /* Sun raster file */
  799.             { "rle", 0x06A1 },            /* Unix RLE image */
  800.             { "rtf", 0x0FFF },            /* Rich Text Format data */
  801.             { "tar", 0x0C46 },            /* tar'd files */
  802.             { "tif", 0x0FF0 },            /* TIFF graphics etc data */
  803.             { "txt", 0x0FFF },            /* Text files */
  804.             { "uue", 0x0DDC },            /* uuencoded file (Sparc archiver) */
  805.             { "wks", 0x0DB0 },            /* Lotus 123 v1 worksheet */
  806.             { "wk1", 0x0DB0 },            /* Lotus 123 v2 worksheet */
  807.             { "xbm", 0x069E },            /* XBM graphics file */
  808.             { "z", 0x0DDC },            /* compress file (Sparc archiver) */
  809.             { "zip", 0x0DDC },            /* ZIP archive (Sparc archiver) */
  810. /*            { "", 0x0FE6 },                \* Unix executable */
  811. /*            { "???", 0x0DB2 },            \* dBase II file */
  812. /*            { "???", 0x0DB4 },             \* SuperCalc III file */
  813.             { "", 0 }                    /* End marker */
  814.             };
  815.  
  816. #elif defined( __IIGS__ )
  817.  
  818. typedef struct {
  819.                char *suffix;            /* The suffix for the file */
  820.                WORD type;                /* The file type */
  821.                LONG auxType;            /* The file auxiliary type */
  822.                } FILE_TYPEINFO;
  823.  
  824. static FILE_TYPEINFO typeInfo[] =
  825.             {
  826.             { "", 0, 0L }                /* End marker */
  827.             };
  828.  
  829. #elif defined( __MAC__ )
  830.  
  831. typedef struct {
  832.                char *suffix;            /* The suffix for the file */
  833.                LONG type;                /* The file type */
  834.                LONG creator;            /* The file creator */
  835.                } FILE_TYPEINFO;
  836.  
  837. static FILE_TYPEINFO typeInfo[] =
  838.             {
  839.             { "c", 'TEXT', 'KAHL' },    /* ThinkC C source */
  840.             { "cpt", 'PACT', 'CPCT' },    /* Compact archive */
  841.             { "ctx", 'PGP ', 'PGP ' },    /* PGP ciphertext file */
  842.             { "dbf", 'F+DB', 'FOX+' },    /* FoxBase+ (.dbf) */
  843.             { "dbt", 'F+DT', 'FOX+' },    /* FoxBase+ (.dbt) */
  844.             { "frm", 'F+FR', 'FOX+' },    /* FoxBase+ (.frm) */
  845.             { "gif", 'GIFf', '8BIM' },    /* GIF graphics file (Photoshop) */
  846.             { "h", 'TEXT', 'KAHL' },    /* ThinkC C source */
  847.             { "hpk", 'HPAK', 'HPAK' },    /* HPACK archive */
  848.             { "idx", 'F+IX', 'FOX+' },    /* FoxBase+ (.idx) */
  849.             { "jpg", 'JFIF', '8BIM' },    /* JPEG graphics file (Photoshop) */
  850.             { "mac", 'PNTG', 'MPNT' },    /* Macpaint graphics file */
  851.             { "p", 'TEXT', 'PJMM' },    /* Think Pascal source */
  852.             { "pas", 'TEXT', 'PJMM' },    /* Think Pascal source */
  853. /*            { "pas", 'TEXT', 'MPS ' },    \* MPW Pascal source */
  854.             { "pit", 'PIT ', 'PIT ' },    /* Packit archive */
  855.             { "rpt", 'F+RP', 'FOX+' },    /* FoxBase+ (.rpt) */
  856.             { "rtf", 'WDBN', 'MSWD' },    /* Rich Text Format (MS Word) */
  857.             { "scx", 'F+FM', 'FOX+' },    /* FoxBase+ (.scx) */
  858.             { "sit", 'SIT!', 'SIT!' },    /* StuffIt archive */
  859.             { "txt", 'TEXT', 'ttxt' },    /* Generic text (TeachText) */
  860.             { "vue", 'F+VU', 'FOX+' },    /* FoxBase+ (.vue) */
  861.             { "xls", 'XLS ', 'XCEL' },    /* Excel worksheet */
  862.             { "xlm", 'XLM ', 'XCEL' },    /* Excel macros */
  863.             { "xlc", 'XLC ', 'XCEL' },    /* Excel charts */
  864.             { "xlw", 'XLW ', 'XCEL' },    /* Excel workspace document */
  865.             { "tif", 'TIFF', 'dpnt' },    /* TIFF graphics file (Desk Paint) */
  866.             { "zip", 'pZIP', 'pZIP' },    /* Info-ZIP archive */
  867. /*            { "doc", 'WDBN', 'MSWD' },    \* MS Word document (sometimes) */
  868. /*            { "???", 'GLOS', 'MSWD' },    \* MS Word glossary */
  869. /*            { "dic", 'DICT', 'MSWD' },    \* MS Word dictionary */
  870.             { "", 0L, 0L },                /* End marker */
  871.             };
  872.  
  873. #endif /* Various OS-specific file type info */
  874.  
  875. void findFileType( FILEHDRLIST *fileInfoPtr )
  876.     {
  877.     TYPE_ASSOC *assocInfoPtr;
  878.     char *suffixPtr, *fileName = fileInfoPtr->fileName;
  879.     int i;
  880.  
  881.     /* If we already have type info, return now */
  882.     if( fileInfoPtr->type )
  883.         return;
  884.  
  885.     /* Find the filename suffix */
  886.     for( suffixPtr = fileName + strlen( fileName ) - 1; \
  887.          suffixPtr != fileName && *suffixPtr != '.'; \
  888.          suffixPtr-- );
  889.     if( suffixPtr++ == fileName )    /* Move past dit, return if no suffix */
  890.         return;
  891.  
  892.     /* First look through the user-defined types.  These can override the
  893.        built-in ones */
  894.     for( assocInfoPtr = typeAssocListHead; assocInfoPtr != NULL; assocInfoPtr = assocInfoPtr->next )
  895.         if( !stricmp( assocInfoPtr->extension, suffixPtr ) )
  896.             {
  897.             fileInfoPtr->type = assocInfoPtr->type;
  898. #if defined( __IIGS__ )
  899.             fileInfoPtr->auxType = assocInfoPtr->auxType;
  900. #elif defined( __MAC__ )
  901.             fileInfoPtr->creator = assocInfoPtr->creator;
  902. #endif /* Setting of other file info */
  903.             return;
  904.             }
  905.  
  906.     /* Then look through the built-in suffix table trying to match the file
  907.        suffix */
  908.     for( i = 0; *typeInfo[ i ].suffix; i++ )
  909.         if( !stricmp( typeInfo[ i ].suffix, suffixPtr ) )
  910.             {
  911.             fileInfoPtr->type = typeInfo[ i ].type;
  912. #if defined( __IIGS__ )
  913.             fileInfoPtr->auxType = typeInfo[ i ].auxType;
  914. #elif defined( __MAC__ )
  915.             fileInfoPtr->creator = typeInfo[ i ].creator;
  916. #endif /* Setting of other file info */
  917.             return;
  918.             }
  919.     }
  920.  
  921. #endif /* __ARC__ || __IIGS__ || __MAC__ */
  922.  
  923. /****************************************************************************
  924. *                                                                            *
  925. *                        Directory-Extraction Routines                        *
  926. *                                                                            *
  927. ****************************************************************************/
  928.  
  929. /* Build the path to a given directory index, truncating names if necessary */
  930.  
  931. char *getPath( WORD dirNo )
  932.     {
  933.     WORD dirStack[ MAX_PATH ];    /* We'll run out of path length before we run
  934.                                    out of directory stack so this is safe */
  935.     static char pathName[ MAX_PATH ];
  936.     int stackIndex = 0, pathLen = 0;
  937.     char *dirNamePtr;
  938.  
  939.     /* Get a chain of directories from the root to the desired directory */
  940.     if( dirNo )
  941.         do
  942.             dirStack[ stackIndex++ ] = dirNo;
  943.         while( ( dirNo = getParent( dirNo ) ) && stackIndex <= MAX_PATH );
  944.  
  945.     /* Build the path to the current directory */
  946.     *pathName = '\0';
  947.     while( stackIndex )
  948.         {
  949. #ifdef __VMS__
  950.         isDirComponent = TRUE;
  951. #endif /* __VMS__ */
  952.         dirNamePtr = truncFileName( getDirName( dirStack[ --stackIndex ] ) );
  953.         if( ( pathLen += strlen( dirNamePtr ) ) >= MAX_PATH - 1 )
  954.             error( PATH_s__TOO_LONG, pathName );
  955.         strcat( pathName, dirNamePtr );
  956.         strcat( pathName, SLASH_STR );
  957.         }
  958.  
  959.     return( pathName );
  960.     }
  961.  
  962. /* Recreate a directory tree stored within an archive */
  963.  
  964. void makeDirTree( void )
  965.     {
  966.     char dirPath[ MAX_PATH ], *pathName;
  967.     int outputPathLen = basePathLen;
  968.     WORD count;
  969.     BOOLEAN inPosition = FALSE;
  970.     long dataLen;
  971.     TAGINFO tagInfo;
  972. #if defined( __MSDOS__ )
  973.     /* Bypass compiler bug */
  974. #elif defined( __MAC__ ) || defined( __OS2__ )
  975.     LONG timeStamp;
  976. #elif defined( __UNIX__ )
  977.     struct utimbuf timeStamp;
  978. #endif /* Various OS-specific attribute variables */
  979.  
  980.     skipDist = 0L;
  981.     strcpy( dirPath, basePath );    /* We have already checked the validity
  982.                                        of basePath in handleArchive() */
  983.     getFirstDir();                    /* Skip archive root directory */
  984.     for( count = getNextDir(); count != END_MARKER; count = getNextDir() )
  985.         {
  986.         /* Get the path to the current directory.  Note that getPath() returns
  987.            SLASH at the end of the pathname.  This is then stomped by the call
  988.            to extractPath() in dirExists(), and is in fact essential since
  989.            otherwise extractPath() will treat the last directory name as a filename */
  990.         pathName = getPath( count );
  991.         if( outputPathLen + strlen( pathName ) >= MAX_PATH )
  992.             error( PATH_ss__TOO_LONG, basePath, pathName );
  993.         strcpy( dirPath + outputPathLen, pathName );
  994.  
  995.         /* Try and create the new directory if it's marked for creation and
  996.            doesn't already exist */
  997.         if( getDirTaggedStatus( count ) && !dirExists( dirPath ) )
  998.             {
  999.             dirPath[ strlen( dirPath ) - 1 ] = '\0';    /* Stomp final SLASH */
  1000. #ifndef GUI
  1001.             hprintfs( MESG_CREATING_DIRECTORY_s, ( fileNameTruncated ) ? \
  1002.                       getDirName( count ) : dirPath );
  1003.             if( fileNameTruncated )
  1004.                 hprintfs( MESG_AS_s, dirPath );
  1005.             hputchars( '\n' );
  1006. #endif /* !GUI */
  1007.             if( hmkdir( dirPath, MKDIR_ATTR ) == IO_ERROR )
  1008.                 error( CANNOT_CREATE_DIR, dirPath );
  1009.  
  1010.             /* Handle tagged information if necessary */
  1011.             if( ( flags & STORE_ATTR ) && ( dataLen = getDirAuxDatalen( count ) ) )
  1012.                 {
  1013.                 /* Move to the correct position in the archive if we're not
  1014.                    already there */
  1015.                 if( !inPosition )
  1016.                     {
  1017.                     movetoDirData();
  1018.                     skipDist = 0L;        /* Skipped data has been bypassed */
  1019.                     inPosition = TRUE;
  1020.                     }
  1021.                 else
  1022.                     skipToData();
  1023.  
  1024.                 /* Grovel through the directory data field looking for
  1025.                    attribute tags */
  1026.                 while( dataLen )
  1027.                     switch( readTag( &dataLen, &tagInfo ) )
  1028.                         {
  1029.                         case TAGCLASS_ATTRIBUTE:
  1030.                             /* Read in the attributes and set them */
  1031.                             hchmod( dirPath, readAttributeData( tagInfo.tagID ) );
  1032.                             break;
  1033.  
  1034. #if defined( __MSDOS__ )
  1035.                         /* Fix compiler bug */
  1036. #elif defined( __MAC__ )
  1037.                         case TAGCLASS_TIME:
  1038.                             /* Read in the file times and set them */
  1039.                             if( tagInfo.tagID == TAG_BACKUP_TIME )
  1040.                                 /* Set file's backup time */
  1041.                                 setBackupTime( dirPath, fgetLong() );
  1042.                             else
  1043.                                 if( tagInfo.tagID == TAG_CREATION_TIME )
  1044.                                     /* Set file's creation time */
  1045.                                     setCreationTime( dirPath, fgetLong() );
  1046.                                 else
  1047.                                     skipSeek( tagInfo.dataLength );
  1048.                             break;
  1049.  
  1050.                         case TAGCLASS_MISC:
  1051.                             if( tagInfo.tagID == TAG_VERSION_NUMBER )
  1052.                                 setVersionNumber( dirPath, ( BYTE ) fgetWord() );
  1053.                             else
  1054.                                 skipSeek( tagInfo.dataLength );
  1055.                             break;
  1056. #elif defined( __OS2__ )
  1057.                         case TAGCLASS_TIME:
  1058.                             /* Read in the file times and set them */
  1059.                             if( tagInfo.tagID == TAG_ACCESS_TIME )
  1060.                                 /* Set file's access time */
  1061.                                 setAccessTime( dirPath, fgetLong() );
  1062.                             else
  1063.                                 if( tagInfo.tagID == TAG_CREATION_TIME )
  1064.                                     /* Set file's creation time */
  1065.                                     setCreationTime( dirPath, fgetLong() );
  1066.                                 else
  1067.                                     skipSeek( tagInfo.dataLength );
  1068.                             break;
  1069.  
  1070.                         case TAGCLASS_ICON:
  1071.                             /* Set the icon EA if possible */
  1072.                             if( tagInfo.tagID == TAG_OS2_ICON )
  1073.                                 setEAinfo( dirPath, &tagInfo, archiveFD );
  1074.                             else
  1075.                                 skipSeek( tagInfo.dataLength );
  1076.                             break;
  1077.  
  1078.                         case TAGCLASS_MISC:
  1079.                             /* Set the longname EA if possible */
  1080.                             if( tagInfo.tagID == TAG_LONGNAME || \
  1081.                                 tagInfo.tagID == TAG_OS2_MISC_EA )
  1082.                                 {
  1083.                                 setEAinfo( dirPath, &tagInfo, archiveFD );
  1084.                                 break;
  1085.                                 }
  1086.                             else
  1087.                                 skipSeek( tagInfo.dataLength );
  1088.                             break;
  1089. #elif defined( __UNIX__ )
  1090.                         case TAGCLASS_TIME:
  1091.                             /* Read in the file times and set them */
  1092.                             if( tagInfo.tagID == TAG_CREATION_TIME )
  1093.                                 {
  1094.                                 timeStamp.actime = fgetLong();
  1095.                                 timeStamp.modtime = getDirTime( count );
  1096.                                 utime( dirPath, &timeStamp );
  1097.                                 }
  1098.                             else
  1099.                                 skipSeek( tagInfo.dataLength );
  1100.                             break;
  1101. #endif /* Various OS-dependant attribute reads */
  1102.  
  1103.                         default:
  1104.                             /* We don't know what to do with this tag, skip it */
  1105.                             skipSeek( tagInfo.dataLength );
  1106.                             break;
  1107.                         }
  1108.                 }
  1109.  
  1110. #ifdef __OS2__
  1111.             /* If we've truncated the directory name, add the full name as
  1112.                an EA */
  1113.             if( fileNameTruncated )
  1114.                 addLongName( dirPath, getDirName( count ) );
  1115. #endif /* __OS2__ */
  1116.  
  1117.             /* Give the directory its real date if necessary.  We don't
  1118.                need to check that the open is successful since we've just
  1119.                created the directory so we know it exists */
  1120.             if( !( flags & TOUCH_FILES ) )
  1121.                 setDirecTime( dirPath, getDirTime( count ) );
  1122.             }
  1123.         else
  1124.             {
  1125.             /* Complain if an entity of this name already exists and skip it
  1126.                and all the files and directories contained within it */
  1127.             if( dirnameConflict )
  1128.                 {
  1129.                 pathName[ strlen( pathName ) - 1 ] = '\0';    /* Stomp final SLASH */
  1130. #ifdef GUI
  1131.                 alert( ALERT_DIRNAME_CONFLICTS_WITH_FILE, pathName );
  1132. #else
  1133.                 hprintf( WARN_DIRNAME_s_CONFLICTS_WITH_FILE, pathName );
  1134. #endif /* GUI */
  1135.                 selectDirNo( count, FALSE );
  1136.                 }
  1137.  
  1138.             /* Skip the data for this directory */
  1139.             skipDist += ( long ) getDirAuxDatalen( count );
  1140.             }
  1141.  
  1142.         /* Make sure we don't get a "Nothing to do" error if there are no
  1143.            files to extract */
  1144.         archiveChanged = TRUE;
  1145.         }
  1146.  
  1147.     /* Move back to the start of the archive if necessary */
  1148.     if( inPosition )
  1149.         vlseek( 0L, SEEK_SET );
  1150.     }
  1151.  
  1152. /****************************************************************************
  1153. *                                                                            *
  1154. *                        Archive Path-Handling Routines                        *
  1155. *                                                                            *
  1156. ****************************************************************************/
  1157.  
  1158. /* Various vars used by several routines */
  1159.  
  1160. static int internalPathLen;
  1161.  
  1162. /* Build the path within the archive to a given file */
  1163.  
  1164. char *buildInternalPath( FILEHDRLIST *infoPtr )
  1165.     {
  1166.     static char internalPath[ MAX_PATH ];
  1167.     char *truncName;
  1168.  
  1169.     strcpy( internalPath, ( flags & STORE_PATH ) ? \
  1170.                             getPath( infoPtr->data.dirIndex ) : "" );
  1171. #ifdef __VMS__
  1172.     isDirComponent = FALSE;
  1173. #endif /* __VMS__ */
  1174.     truncName = truncFileName( infoPtr->fileName );
  1175.     if( ( internalPathLen = strlen( internalPath ) + strlen( truncName ) ) >= MAX_PATH )
  1176.         error( PATH_ss_TOO_LONG, internalPath, truncName );
  1177.     strcat( internalPath, truncName );
  1178.     return( internalPath );
  1179.     }
  1180.  
  1181. /* Build the path outside the archive to a given file */
  1182.  
  1183. char *buildExternalPath( FILEHDRLIST *infoPtr )
  1184.     {
  1185.     static char externalPath[ MAX_PATH ];
  1186.     char *pathPtr;
  1187.  
  1188.     strcpy( externalPath, basePath );
  1189.     pathPtr = buildInternalPath( infoPtr );
  1190.     if( basePathLen + internalPathLen >= MAX_PATH )
  1191.         error( PATH_ss_TOO_LONG, externalPath, pathPtr );
  1192.     strcat( externalPath, pathPtr );
  1193.     return( externalPath );
  1194.     }
  1195.  
  1196. BOOLEAN isTruncated( void )
  1197.     {
  1198.     return( fileNameTruncated );
  1199.     }
  1200.